Python Web应用框架Django编程笔记

74次阅读
没有评论

共计 8256 个字符,预计需要花费 21 分钟才能阅读完成。

安装

pip install django

安装完,在 Python 安装目录 Libsite-packages 下会多一个文件夹 django(框架源码),在 Scripts 文件夹下会多一个文件 django-admin.exe(用来创建 django 项目)。

创建项目

打开终端,进入某个目录(项目路径),执行命令创建项目:django-admin startproject mysite

运行后将在目录下生成一个 mysite 目录,也就是 Django 项目的根目录。

创建应用

在 Django 中,每一个应用(app)都是一个 Python 包。

cd mysite
python manage.py startapp app

运行后会自动生成 app 文件夹:

app
|   admin.py    # Django 默认提供 admin 后台管理
|   apps.py     # app 启动类
|   models.py   # 对数据库操作
|   tests.py    # 单元测试
|   views.py    # 视图函数
|   __init__.py
|
---migrations  # 数据框变更记录
        __init__.py

注册应用

打开mysite/setting.py,在 INSTALLED_APPS 当中注册 app 模块:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app',
]

设置

时区和语言

修改项目的 settings.py 文件如下:

LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_TZ = False

admin 后台

设置 admin 后台应用显示为中文,需要修改应用目录下的 apps.py 文件:

class LoginConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'login'
    verbose_name = '用户管理' # 新增

设置 admin 后台首页的网页标题,需要修改应用目录下的的 admin.py 文件:

admin.site.site_title = '后台管理' # 修改标题
admin.site.site_header = '后台管理' # 修改 header

本地配置

首先创建自己的本地配置文件:local_settings.py。在 settings.py 中导入:

try:
    from .local_settings import *
except ImportError:
    pass

切记:给别人代码时,不要给local_settings.py

快速上手

URL 和视图配置

打开mysite/urls.py,修改为:

from django.contrib import admin
from django.urls import path

from app import views  # 导入 views

urlpatterns = [path('admin/', admin.site.urls),

    # 域名 /index/ -> index 函数,即绑定 URL 与视图函数
    path('index/', views.index),
]

编写视图函数

视图,即具体的业务代码,打开app/views.py,修改为:

from django.shortcuts import render, HttpResponse

def index(requests):
    return HttpResponse('欢迎使用')

启动项目

命令行启动

python manage.py runserver

Django 的服务器默认运行在 8000 端口,如果想指定端口,命令如下:

python manage.py runserver 8080

templates 模板

在 app 文件夹下新建 templates 文件夹,然后在 templates 文件夹下新建文件user_list

在文件 mysite/urls.py 中添加 URL:

path('user/list', views.user_list, {'name': '李小龙'}),

在文件 app/views.py 中添加函数:

def user_list(request):
    # 去 app 目录下的 templates 目录寻找 user_list
    return render(request, 'user_list')

静态文件

在 app 文件夹下新建 static 文件夹,然后在 static 目录下创建 css 目录,js 目录,img 目录,plugin 目录,分别放 css 文件,js 文件,图片和插件。

在模板中使用需要加入 {% load static %} 代码,以下实例从静态目录中引入图片,修改 user_list 为:

{% load static %}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title> 用户列表 </title>
</head>
<body>
    <h1>{{name}}</h1>
    <img src="{% static 'img/logo.webp' %}" alt="logo">
</body>
</html>

模板语法

本质上是在 html 中写一些占位符,由数据对这些占位符进行替换和处理。

变量相关

在文件 mysite/urls.py 中添加 URL:

path('tpl/', views.tpl),

在文件 app/views.py 中添加函数:

def tpl(request):
    name = '李小龙'
    roles = ['管理员', 'CEO', '保安']
    user_info = {'name': " 李小龙 ", 'sex': '女', 'role': '保安'}
    return render(request, 'tpl', {'n1': name, 'n2': roles, 'n3': user_info})

render 使用了一个字典作为参数,字典中元素的键值 n1 对应了模板中的变量 {{n1}}。

app/templates 目录下新建 tpl 文件,代码如下:

<h1>{{n1}}</h1>
<h1>{{n2}}</h1>
<h1>{{n2.0}}</h1>
<h1>{{n2.1}}</h1>
<h1>{{n3.name}}</h1>
<h1>{{n3.sex}}</h1>

访问 http://127.0.0.1:8000/tpl/,查看页面输出结果。

逻辑相关

tpl 文件添加如下代码:

<hr/>
<div>
    {% for item in n2 %}
        <span>{{item}}</span>
    {% endfor %}
</div>
<hr/>
<ul>
    {% for k,v in n3.items %}
        <li>{{k}} = {{v}}</li>
    {% endfor %}
</ul>

访问 http://127.0.0.1:8000/tpl/,查看页面输出结果。

模板继承

父模板

{% block 名称 %}
预留给子模板的区域,可以设置默认内容
{% endblock 名称 %}

子模板

{% extends " 父模板路径 " %}

子模板使用标签 extends 继承父模板。

接下来在 templates 目录中添加 base 文件,代码如下:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title> 编程笔记 </title>
</head>
<body>
    <h1>Hello World!</h1>
    <p> 编程笔记 Django 测试。</p>
    {% block mainbody %}
       <p>original</p>
    {% endblock %}
</body>
</html>

index 继承 base,index 修改后的代码如下:

{%extends "base" %}

{% block mainbody %}
<p> 继承了 base 文件。</p>
{% endblock %}

访问 http://127.0.0.1:8000/index 查看页面输出结果。可以看到,这里相同名字的 block 标签用于替换 base 的相应 block。

请求和响应

在文件 mysite/urls.py 中添加 URL:

path('something/', views.something),

在文件 app/views.py 中添加函数:

from django.shortcuts import redirect
def something(request):
    # 获取请求方式 GET/POST
    print(request.method)

    # 在 url 传递值 /something/?n1= 李小龙
    print(request.GET)

    # 在请求体中提交数据
    print(request.POST)

    # 重定向
    return redirect('https://www.baidu.com/')

访问 http://127.0.0.1:8000/something/,查看页面输出结果。

Django 模型

MySQL 是 Web 应用中最常用的数据库,这里以 MySQL 作为实例进行介绍。如果没安装 mysql 驱动,执行以下命令安装:

pip3 install pymysql

这里作个说明,pymysql 和 mysqlclient 是目前 Python 连接 MySQL 的主流方式。

区别是:mysqlclient 速度比 pymysql 快;pymysql 更加简单易使用。

如果是大型项目,当然是用 mysqlclient,该驱动性能比较优异;中小项目用 pymysql 就可以了,毕竟是使用 Python 写的,契合度更高。

数据库配置

在项目的 settings.py 文件中找到 DATABASES 配置项,将其信息修改为:

DATABASES = { 
    'default': 
    { 
        'ENGINE': 'django.db.backends.mysql', # 数据库引擎
        'NAME': 'django', # 数据库名称
        'HOST': '127.0.0.1', # 数据库地址,本机 ip 地址
        'PORT': 3306, # 端口 
        'USER': 'root', # 数据库用户名
        'PASSWORD': '', # 数据库密码
    }  
}

接下来,告诉 Django 使用 pymysql 模块连接 mysql 数据库:

## 在与 settings.py 同级目录下的 __init__.py 中引入模块和进行配置
import pymysql
pymysql.install_as_MySQLdb()

操作表

创建表

在 models.py 文件中:

class UserInfo(models.Model):
    name = models.CharField(max_length=32)
    # 设置默认值
    age = models.IntegerField(default=0)
    # 允许为空
    data = models.DateField(null=True, blank=True)

执行命令:

## 创建应用的迁移数据文件
python manage.py makemigrations app
## 生成数据库表
python manage.py migrate

修改表

在文件 urls.py 中添加 URL:

path('orm/', views.orm),

在文件 views.py 中添加函数:

def orm(request):
    # 新增
    UserInfo.objects.create(name='李小龙', age=18, date='2022-04-04')

    # 删除
    # UserInfo.objects.filter(id=1).delete()
    # UserInfo.objects.all().delete()

    # 查询数据,data_list 是 QuerySet 类型
    # data_list = UserInfo.objects.all()
    # for obj in data_list:
    #     print(obj.name, obj.age, obj.date)
    # 获取第一条数据
    # row_obj = UserInfo.objects.filter(id=5).first()
    # print(row_obj.id, row_obj.name)

    # 更新数据
    # UserInfo.objects.all().update(age=20)
    # UserInfo.objects.filter(id=5).update(age=20)

    return HttpResponse('成功')

访问 http://127.0.0.1:8000/orm/,查看页面输出结果。

外键删除

一个模型如果使用了外键,那么在对方那个模型被删掉后,该进行什么样的操作,可通过 on_delete 来指定。指定的类型如下:

  • CASCADE:级联操作。如果外键对应的那条数据被删除了,那么这条数据也会被删除
  • PROTECT:受保护。即只要这条数据引用了外键的那条数据,那么就不能删除外键的那条数据。如果我们强行删除,Django 就会报错
  • SET_NULL:设置为空。如果外键的那条数据被删除了,那么在本条数据上就将这个字段设置为空。如果设置这个选项,前提是要指定这个字段可以为空
  • SET_DEFAULT:设置默认值。如果外键的那条数据被删除了,那么本条数据上就将这个字段设置为默认值。如果设置这个选项,前提是要指定这个字段一个默认值。
  • SET():如果外键的那条数据被删除了,那么将会获取 SET 函数中的值来作为这个外键的值。SET 函数可以接收一个可以调用的对象(比如函数或者方法),如果是可以调用的对象,那么会将这个对象调用后的结果作为值返回回去。可以不用指定默认值
  • DO_NOTHING:不采取任何行为。一切全看数据库级别的约束

数据库接口

QuerySet 是 Django 提供的强大的数据库接口(API)。从数据库中查询得到的结果一般是一个集合,这个集合叫做 QuerySet。

QuerySet 惰性

所谓惰性,就是不会主动去执行。创建 QuerySet 的行为(语句)不会马上执行 sql,而是返回一个 QuerySet(查询结果集对象),只有在调用 QuerySet 的时候才会涉及到数据库操作。

QuerySet 缓存

下例中例 1 比例 2 要好,因为在你打印文章标题后,Django 不仅执行了查询,还把查询到的 article_list 放在了缓存里,因此这个 article_list 是可以复用的。例 2 就不行了。

## Example 1: Good
article_list = Article.objects.filter(title_contains="django")
for article in article_list:
    print(article.title)

## Example 2: Bad
for article in Article.objects.filter(title_contains="django"):
    print(article.title)

在 loop 前加个 if 判断,防止 article_list 是个空数据集:

article_list = Article.objects.filter(title_contains="django")
if article_list:
    for article in article_list:
        print(article.title)
else:
    print("No records")

如果只是希望了解查询的结果是否存在,而不需要使用整个数据集,这时 if 触发整个 queryset 的缓存变成了一件坏事情。这时可以用 exists()方法。与 if 判断不同,exists 只会检查查询结果是否存在,返回 True 或 False,而不会缓存 article_list。

article_list = Article.objects.filter(title_contains="django")
if article_list.exists():
    print("Records found.")
else:
    print("No records")

统计查询结果数量

len()与 count()均能统计查询结果的数量。一般来说 count 更快,因为它是从数据库层面直接获取查询结果的数量,而不是返回整个数据集,而 len 会导致 queryset 的执行,需要将整个 queryset 载入内存后才能统计其长度。但事情也没有绝对,如果数据集 queryset 已经在缓存里了,使用 len 更快,因为它不需要跟数据库再次打交道。

下面三个例子中,只有例 2 最差,尽量不要用:

## Example 1: Good
count = Article.objects.filter(title_contains="django").count()

## Example 2:Bad
count = Article.objects.filter(title_contains="django").len()

## Example 3: Good
article_list = Article.objects.filter(title_contains="django")
if article_list:
    print("{} records found.".format(article_list.len()))

按需查询数据

当查询到的 queryset 非常大时,会大量占用内存(缓存)。我们可以使用 values 和 value_list 方法按需提取数据。

article_list = Article.objects.filter(title_contains="django").values('title')
if article_list:
    print(article.title)

article_list = Article.objects.filter(title_contains="django").values_list('id', 'title')
if article_list:
    print(article.title)

还可以使用 defer 和 only 这两个查询方法来实现按需查询数据。

defer 方法

defer 方法的用途是查询数据库时跳过指定的字段,防止不要的字段载入内存,从而节省空间:

## 查询时跳过 id 和 title 字段
Article.objects.defer('id', 'title')

该方法可以和 filter, exclude 方法联用,其顺序无关紧要,如下所示:

Article.objects.defer(‘id’).filter(title_contains=”django”).defer(‘title’)

only 方法

only 方法与 defer 方法作用类似,只不过 only 方法是指定需要载入的字段。比如下面查询将只会载入 id 和 title。

Article.objects.only('id', 'title')

使用 only 方法要注意它的顺序,它的执行以最后一个为准。下例将只会载入 pub_date:

Article.objects.only('id', 'title').only('pub_date')

only 方法与 defer 方法可以联用,但要注意先后顺序。下例只会载入 title:

Article.objects.only('id', 'title').defer('id')

更新数据库

更新部分字段

更新数据库部分字段更好的方式是用 update,而不是 save 方法。

## Example 1: Good
Article.objects.filter(id=10).update(title='Django')

## Example 2: Bad
article = Article.objects.get(id=10)
Article.title = "Django"
article.save()
批量创建或更新数据

Django 提供的 bulk_create 和 bulk_update 方法可以一次添加或更新多条数据,效率要高很多,如下所示:

## 内存生成多个对象实例
articles = [Article(title="title1", body="body1"), Article(title="title2", body="body2"), Article(title="title3", body="body3")]

## 执行一次 SQL 插入数据
Article.objects.bulk_create(articles)

命令

## 创建超级管理员
python manage.py createsuperuser
## 无需启动 Django 操作数据库中某个表的数据
python manage.py shell

正文完
 0
三毛笔记
版权声明:本站原创文章,由 三毛笔记 于2023-08-13发表,共计8256字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)